// -------------------------------------------------------------
//      GEANT4 project of test cxx api
//
//      Modified: 2025.11.06
//
// -------------------------------------------------------------

#include <fmt/format.h>

#include <G4AnalysisManagerState.hh>
#include <G4Threading.hh>
#include <G4UserRunAction.hh>
#include <G4VUserPhysicsList.hh>
#include <iostream>

#include "ActionInitialization.hh"
#include "CliArgs.hh"
#include "DetectorConstruction.hh"
#include "G4RunManager.hh"
#include "G4RunManagerFactory.hh"
#include "G4ScoringManager.hh"
#include "G4SystemOfUnits.hh"
#include "G4Types.hh"
#include "G4UIExecutive.hh"
#include "G4UImanager.hh"
#include "G4VisExecutive.hh"
#include "MyConstant.hh"
#include "MyInitial.hh"
#include "MyMacros.hh"
#include "PhysicsList.hh"
#include "PrimaryGeneratorAction.hh"
#include "Randomize.hh"
#include "RunData.hh"

//#include "RunAction.hh"
// #include "G4PSEnergyDeposit.hh"
// #include "PSSDoseAveragedLET.hh"
using fmt::format;

constexpr G4int nEvent = 1000;

struct BatchIn
{
    G4RunManager* runMgr;
    ActionInitialization* actionInit;
    G4ScoringManager* scoreMgr;
    PhysicsList* phyList;
};

void BatchRun(const BatchIn& batchIn)
{
    MYG4TID(tid);

    auto* const runMgr = batchIn.runMgr;
    auto* const actionInit = batchIn.actionInit;
    auto* const scoreMgr = batchIn.scoreMgr;
    auto* const phyList = batchIn.phyList;
    //auto* const actInit = runMgr->GetNonConstUserActionInitialization();
    // G4UImanager* const ui = G4UImanager::GetUIpointer();

    ///==========================
    DINFOLN("````````````````````````````````````````````[tid {} main] RunManager Initialize()", tid);
    runMgr->Initialize();

    //============================= Set cut value
    DINFOLN("````````````````````````````````````````````[tid {} main] Set cut value", tid);
    // 给所有粒子、所有区域统一设 cut; 0.13197 mm -> 0.13197*mm 自动转成长度
    // const G4double cut = 0.13197 * mm;
    const G4double cut = 0.05 * mm;
    phyList->SetDefaultCutValue(cut);

    ///========================== Energy layers
    //auto* const primGen = actionInit->GetPrimGenAction();
    std::vector<G4double> energies{
        100.0 * MeV, 500.0 * MeV, 1000.0 * MeV, 200.0 * MeV, 300.0 * MeV, 400.0 * MeV,
    };
    for (auto engy : energies)
    {
        RunData::Instance().SetEnergy(engy);
        // DINFOLN("[tid {} main] Set particle energy", tid);
        //primGen->GetParticleGun()->SetParticleEnergy(engy);  // 每轮改能量
        DINFOLN("````````````````````````````````````````````[tid {} main] Beam On", tid);
        // G4cout << "[main] Set particle energy\n" << std::flush;
        runMgr->BeamOn(1000);  // 再跑 1000 事件
    }
}

int main(int argc, char** argv)
{
    /// ====================== 解析命令行参数
    CliArgs cliArgs(argc, argv);
    if (!cliArgs.ok) return 0;  // 已打印 help 或错误

    myinit::InitLogger();

    ////=================================== Set the Random engine
    const auto seed = (G4int)time(NULL);
    G4Random::setTheSeed(seed);
    G4cout << G4endl << " Initial seed = " << seed << G4endl << G4endl;

    ///=================================== Run manager
    DINFOLN("[main] CreateRunManager()");
    auto* runManager = G4RunManagerFactory::CreateRunManager();
    // const G4int nThreads = std::min(G4Threading::G4GetNumberOfCores(), 4);
    const G4int nThreads = 1;
    runManager->SetNumberOfThreads(nThreads);
    G4cout << "===== Geant4 is started with " << runManager->GetNumberOfThreads() << " threads =====" << G4endl;

    ///=================================== Scoring manager
    auto* const scoreMgr = G4ScoringManager::GetScoringManager();
    scoreMgr->SetVerboseLevel(1);

    ///=================================== Detector construction
    auto* const detect = new DetectorConstruction();
    DINFOLN("[main] Set User DetectorConstruction");
    runManager->SetUserInitialization(detect);

    ///=================================== Physics lists
    auto* const phyList = new PhysicsList();
    DINFOLN("[main] Set User PhysicsList");
    runManager->SetUserInitialization(phyList);

    //===================================== Action Initialization
    auto* const actionInit = new ActionInitialization(detect);
    DINFOLN("[main] Set user ActionInitialization");
    runManager->SetUserInitialization(actionInit);

#if 0
    //===================================== RunAction
    auto* const runAction = new RunAction();
    DINFOLN("[main] Set user RunAction");
    runManager->SetUserAction(runAction);
#endif

    //===================================== Qt 交互
    if (cliArgs.interactive)
    {
        runManager->Initialize();
        G4UIExecutive session(argc, argv);
        G4VisExecutive visExecutive;
        visExecutive.Initialize();
        G4UImanager* const ui = G4UImanager::GetUIpointer();
        ui->ApplyCommand("/control/execute init_vis.mac");
        session.SessionStart();
    }
    //===================================== 批处理宏
    else if (!cliArgs.macro.empty())
    {
        G4UImanager* const ui = G4UImanager::GetUIpointer();
        auto ret = ui->ApplyCommand(format("/control/execute {}", cliArgs.macro));
    }
    //===================================== 默认任务, 批量扫描
    else
    {
        BatchRun({
            runManager,
            actionInit,
            scoreMgr,
            phyList,
        });
    }

    delete runManager;
    return 0;
}